interface IPeople {
  name: string;
  age: number;
}

// 通过extends 对T作出了约束
function log<T extends IPeople>(p: T): T {
  return p;
}

log({ name: "hah", age: 11 });

// 依然通过extends 配合 keyof 对 K 作出类型约束。
function getValue<T, K extends keyof T>(obj: T, prop: K) {
  return obj[prop];
}

const a11: IPeople = {
  name: "pal",
  age: 11
};
// 已经作出类型约束之后，就不用再写T了，直接调用就可以(因为类型推断)
getValue(a11, "name");
getValue(a11, "age");
// 写全的形式
getValue<IPeople, "name">(a11, "name");
getValue<IPeople, "age">(a11, "age");

// 交叉类型 & - 同时具有 之和

// 联合类型 | - 可选 之一

// ![](https://i.loli.net/2019/05/05/5cce9862a1fe4.png)
interface IBird7 {
  fly(): void;
  eat(): void;
}
interface IFish7 {
  swim(): void;
  eat(): void;
}

class Bird7 implements IBird7 {
  fly() {
    console.log("fly");
  }
  eat() {
    console.log("bird eat");
  }
}

class Fish7 implements IFish7 {
  swim() {
    console.log("swim");
  }
  eat() {
    console.log("fish eat");
  }
}
// 联合类型注意点!!
function eatAnimal(): IFish7 | IBird7 {
  return Math.random() > 0.5 ? new Bird7() : new Fish7();
}
// eatAnimal().eat();
// 这里就有问题了, 因为不确定最终的返回值类型是什么，所以只能调用它们的公共方法
// eatAnimal().fly();

// 解决方式
// 1. 可以使用断言 as
// ...
// 2. 类型保护 - 类型谓词 / 或者直接使用 typeof 和 instanceof 也可以
function isNumber(x: any): x is number {
  return typeof x === "number";
}
function isFish7(x: IFish7 | IBird7): x is IFish7 {
  return (x as IFish7).swim !== undefined;
}

const animal7 = eatAnimal();

// 然后，我们就可以这样
if (isFish7(animal7)) {
  animal7.swim();
} else {
  animal7.fly();
}
